
#include <iostream>
#include <string>
#include <algorithm>
#include <boost\lexical_cast.hpp>
#include <boost\tokenizer.hpp>


#include "stringer.h"
#include "material.h"
#include "rivet.h"
#include "box.h"
#include "wingbox.h"
#include "commandexecutor.h"


void SetSpacings(wingbox& design, std::vector<std::string> token)
{
	try{

		if (token.size() == 0) {
			token = std::vector<std::string>(design.numberTopStringers()-2, "-1");
		}

		if (token.size() == design.numberTopStringers()-2) {
			std::vector<double> spacings;
			std::for_each(token.begin(), token.end(), [&spacings] (const std::string& v) {
				double n(boost::lexical_cast<double>(v));
				if (n != -1) n/= 1000.0;
				spacings.push_back( n);
			});
			if (!design.setStringerSpacings(spacings)) {
				std::cout << " ERROR: STRINGER CONFIG AT ROOT NOT POSSIBLE" <<std::endl;
			}
			design.createRivets();
			design.displaydata();
		} else {
			std::cout << "wrong number of spacings (" << std::distance(token.begin(), token.end()) << " given, " << design.numberTopStringers()-2 << " required), retry: "<< std::endl;
		}
	} catch (boost::bad_lexical_cast err) {
		std::cout << "bad formatted numbers, retry: "<< std::endl;
	}
}

void Save(wingbox& design, std::vector<std::string> token)
{
	try {
		if (token.size() > 0) {
		
			design.saveBox(token[0]);
		} else {
			design.saveBox("designs.csv");
		}
	} catch (...) {
		std::cout << "data can't be saved, random read/write error" << std::endl;
	}
}

void ChangeMaterial(wingbox& design, std::vector<std::string> token)
{
	try{
		if (token.size() == 4) {
			int i(0);
			double density = boost::lexical_cast<double>(token[i++]);
			double youngsmodulus = boost::lexical_cast<double>(token[i++]);
			double yieldstress = boost::lexical_cast<double>(token[i++]);
			double ultimatestress = boost::lexical_cast<double>(token[i++]);

			design.changeMaterial(createMaterial(density, youngsmodulus, yieldstress, ultimatestress));
		} else {
			std::cout << "wrong number of arguments (" << std::distance(token.begin(), token.end()) << " given, " << design.numberTopStringers()-2 << " required)"<< std::endl;
		}
	} catch (boost::bad_lexical_cast err) {
		std::cout << "bad formatted numbers "<< std::endl;
	}
}
void ChangeStringer(wingbox& design, std::vector<std::string> token)
{
	try{
		if (token.size() == 3) {
			
			int i(0);
			int n = boost::lexical_cast<int>(token[i++]);
			double w = boost::lexical_cast<double>(token[i++]);
			double t = boost::lexical_cast<double>(token[i++]);

			
			design.changeStringer(n, w, t);
			design.displaydata();
		} else {
			std::cout << "wrong number of arguments (" << std::distance(token.begin(), token.end()) << " given, " << design.numberTopStringers()-2 << " required)"<< std::endl;
		}
	} catch (boost::bad_lexical_cast err) {
		std::cout << "bad formatted numbers "<< std::endl;
	}
}
void ChangeBox(wingbox& design, std::vector<std::string> token)
{
	try{
		if (std::distance(token.begin(), token.end()) == 4) {
			
			int i(0);
			double w = boost::lexical_cast<double>(token[i++]);
			double h = boost::lexical_cast<double>(token[i++]);
			double t = boost::lexical_cast<double>(token[i++]);
			double l = boost::lexical_cast<double>(token[i++]);
			double s = boost::lexical_cast<double>(token[i++]);


			design.changeBox(w, h, t, l, s);
		} else {
			std::cout << "wrong number of arguments (" << std::distance(token.begin(), token.end()) << " given, " << design.numberTopStringers()-2 << " required)"<< std::endl;
		}
	} catch (boost::bad_lexical_cast err) {
		std::cout << "bad formatted numbers "<< std::endl;
	}
}

void ChangeRibs(wingbox& design, std::vector<std::string> token)
{
	try{
		if (token.size() == 2) {
			
			int i(0);
			double r1 = boost::lexical_cast<double>(token[i++]);
			double r2 = boost::lexical_cast<double>(token[i++]);

			design.changeRibPositions(r1, r2);
		} else {
			std::cout << "wrong number of arguments (" << std::distance(token.begin(), token.end()) << " given, " << design.numberTopStringers()-2 << " required)"<< std::endl;
		}
	} catch (boost::bad_lexical_cast err) {
		std::cout << "bad formatted numbers "<< std::endl;
	}
}
void ChangeSafety(wingbox& design, std::vector<std::string> token)
{
	try{
		double ss,sr;
		if (token.size() == 1) {
			ss = boost::lexical_cast<double>(token[0]);
			sr = boost::lexical_cast<double>(token[0]);
		} else 	if (token.size() == 2) {
			
			int i(0);
			ss = boost::lexical_cast<double>(token[i++]);
			sr = boost::lexical_cast<double>(token[i++]);

			
		} else {
			std::cout << "wrong number of arguments (" << std::distance(token.begin(), token.end()) << " given, " << design.numberTopStringers()-2 << " required)"<< std::endl;
			return;
		}
		design.changeSafetyFactors(ss, sr);
	} catch (boost::bad_lexical_cast err) {
		std::cout << "bad formatted numbers "<< std::endl;
	}
}
void ChangeLoads(wingbox& design, std::vector<std::string> token)
{
	try{
		if (token.size() == 2) {
			
			int i(0);
			double bl = boost::lexical_cast<double>(token[i++]);
			double ul = boost::lexical_cast<double>(token[i++]);

			design.changeMaximumLoadings(bl, ul);
		} else {
			std::cout << "wrong number of arguments (" << std::distance(token.begin(), token.end()) << " given, " << design.numberTopStringers()-2 << " required)"<< std::endl;
		}
	} catch (boost::bad_lexical_cast err) {
		std::cout << "bad formatted numbers "<< std::endl;
	}
}
void ChangeBucklingConstants(wingbox& design, std::vector<std::string> token)
{
	try{
		if (token.size() == 2) {
			
			int i(0);
			double kc = boost::lexical_cast<double>(token[i++]);
			double c = boost::lexical_cast<double>(token[i++]);

			design.changeBucklingConstants(kc, c);
		} else {
			std::cout << "wrong number of arguments (" << std::distance(token.begin(), token.end()) << " given, " << design.numberTopStringers()-2 << " required)"<< std::endl;
		}
	} catch (boost::bad_lexical_cast err) {
		std::cout << "bad formatted numbers "<< std::endl;
	}
}
void ChangeClamps(wingbox& design, std::vector<std::string> token)
{
	try{
		if (token.size() == 2) {
			
			int i(0);
			double cf = boost::lexical_cast<double>(token[i++]);
			double ct = boost::lexical_cast<double>(token[i++]);

			design.changeClamps(cf, ct);
		} else {
			std::cout << "wrong number of arguments (" << std::distance(token.begin(), token.end()) << " given, " << design.numberTopStringers()-2 << " required)"<< std::endl;
		}
	} catch (boost::bad_lexical_cast err) {
		std::cout << "bad formatted numbers "<< std::endl;
	}
}
void ChangeName(wingbox& design, std::vector<std::string> token)
{
	if (token.size() == 1) {
		design.changeName(token[0]);
	} else {
		std::cout << "wrong number of arguments (" << std::distance(token.begin(), token.end()) << " given, " << design.numberTopStringers()-2 << " required)"<< std::endl;
	}
}

void Recalc(wingbox& design, std::vector<std::string> token)
{
	design.findBestRemovalOrder();
	design.createRivets();
	design.displaydata();
}


int main() 
{
	//material aluminium = {2780, 72400e6, 345e6, 483e6};
	material aluminium = createMaterial(2.78, 72400, 345, 483);

	stringer s(15, 1.5, 1500, aluminium);
	box b(400, 150, 0.8, 1500, 30, aluminium);
	rivet r;

	wingbox design(8, s, b, r, 1100, 600, 2500, 5000, 1.05, 1.15);
	design.changeName("initial");

	std::vector<double> spacings(design.numberTopStringers()-2, -1);
	if (!design.setStringerSpacings(spacings)) {
		std::cout << " ERROR: STRINGER CONFIG AT ROOT NOT POSSIBLE" <<std::endl;
	} 
	design.createRivets();
	
	design.displaydata();
	try {
		design.saveBox("designs.csv");
	} catch (...) {
		std::cout << "data can't be saved, random read/write error" << std::endl;
	}

	std::string input("");


	exec performer(&design);
	//performer.RegisterFunc("calc", &SetSpacings, "Calculates the new wingbox" );
	performer.RegisterFunc("space", &SetSpacings, "Give new test configuration of all stringers (separate by commas) \n" \
									"use '-1' to describe maximum possible length\nuse '0' to describe old length \n " \
									"This functions also recalculates the whole wingbox\n" \
									"Number of spacing required is the amount of stringers - 2 (last spacing is implied)\n"\
									"example: space 80,30,40,50"
						  );
	performer.RegisterFunc("store", &Save, "Stores the calculated wingbox to the file\n" \
		"example: store design.csv"
						  );
	performer.RegisterFunc("recalc", &Save, "Simply calculates values of current configuration\n" \
		"example: recalc"
						  );
	performer.RegisterFunc("change_mat", &ChangeMaterial, "Changes material\n" \
		"syntax: 'density [g/cm3], E [MPa], yield [MPa], ult [MPa]'\n"\
		"example: change_mat 2.78,72400,345,483"
						  );
	performer.RegisterFunc("change_box", &ChangeBox, "Changes box dimensions\n" \
		"syntax: 'w[mm], h[mm], t[mm], lh[mm], sweep[degrees]'\n"\
		"example: change_box 400,150,0.8,1500,30"
						  );
	performer.RegisterFunc("change_string", &ChangeStringer, "Changes stringer dimensions and number\n" \
		"syntax: 'n,width [mm], thickness[mm]'\n"\
		"example: change_string 8,15,1.5"
						  );
	performer.RegisterFunc("change_rib", &ChangeRibs, "Changes rib location\n" \
		"syntax: 'rib1[mm], rib2[mm]'\n"\
		"example: change_rib 1100,600"
						  );
	performer.RegisterFunc("change_load", &ChangeLoads, "Changes loads\n" \
		"syntax: 'buckling[mm], ultimate[mm]'\n"\
		"example: change_load 2500,5000"
						  );
	performer.RegisterFunc("change_safety", &ChangeSafety, "Changes safety factors\n" \
		"syntax: 'sheetbuckling, interrivet'\n"\
		"example: change_safety 1.05,1.15"
						  );
	performer.RegisterFunc("change_kc", &ChangeBucklingConstants, "Changes KC and endfixity\n" \
		"syntax: 'kc, endfixity'\n"\
		"example: change_kc 3.6,2.1"
						  );
	performer.RegisterFunc("change_clamps", &ChangeClamps, "Changes clamp sizes\n" \
		"syntax: 'rootclamp[mm], tipclamp[mm]'\n"\
		"example: change_kc 110,65"
						  );
	performer.RegisterFunc("rename", &ChangeName, "renames the wingbox for identification in save\n" \
		"example: rename updated"
						  );
	do {
		
		std:: cout << "\ntype 'exit' to end, type 'help' for a list of commands"  <<std::endl;
		std::getline(std::cin, input);
	} while (performer.perform(input) == true );
}